namespace SimpleWebServices.Web.UI.Html
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    using System.Web;
    using System.Xml;
    using System.Xml.Serialization;
    using SimpleWebServices.Models;
    using SimpleWebServices.Web.UI.Html.Elements;
    using SimpleWebServices.Web.UI.Html.Validation;

    [XmlRoot("container")]
    public abstract class FormContainer : Resource, IContainForm
    {
        protected FormContainer()
        {
            this.ValidatorSettings = new ValidatorSettingsCollection();
        }

        [XmlElement("form")]
        public Form Form
        {
            get;
            set;
        }

        [XmlElement("validators")]
        public ValidatorSettingsCollection ValidatorSettings
        {
            get;
            set;
        }

        public abstract void Complete(FormSession session);

        public virtual string Render(int tabIndex)
        {
            if (1 > tabIndex)
            {
                throw new ArgumentOutOfRangeException("tabIndex");
            }

            foreach (var item in ToTabIndexCollection(this.Form))
            {
                item.Value.TabIndex = tabIndex++;
            }

            if (this.Token.HasValue && !Guid.Empty.Equals(this.Token))
            {
                DisplayErrors(this.Form);
                this.Cacheability = HttpCacheability.NoCache;
                this.Expires = null;
                this.Etag = null;

                if (this.Form.Action.IsAbsoluteUri)
                {
                    this.Form.Action = new Uri(string.Concat(this.Form.Action.AbsoluteUri, '?', this.Token));
                }
                else
                {
                    this.Form.Action = new Uri(string.Concat(this.Form.Action.OriginalString, '?', this.Token), UriKind.Relative);
                }
            }

            return this.Form.Render();
        }

        public abstract T Template<T>()
            where T : IContainForm;

        public virtual T Populated<T>()
            where T : IContainForm
        {
            T result = this.Template<T>();

            if (null == result)
            {
                throw new ConfigurationErrorsException("No form template has been defined.");
            }

            result.Token = HttpContextFactory.Current.Request.Token();
            if (result.Token.HasValue && !Guid.Empty.Equals(result.Token))
            {
                result.Form.Retrieve(result.Token);
            }

            if (null != result.ValidatorSettings)
            {
                foreach (ValidatorSettings item in result.ValidatorSettings)
                {
                    item.AddValidator(result.Form);
                }
            }

            return (T)result;
        }

        private static SortedDictionary<int, IAttributeTabIndex> ToTabIndexCollection(IInnerElements obj)
        {
            SortedDictionary<int, IAttributeTabIndex> result = new SortedDictionary<int, IAttributeTabIndex>();

            int index = 1;
            int highest = 1;
            List<IAttributeTabIndex> list = ToTabIndexedList(obj);
            foreach (var item in list.Where(x => x.TabIndex.HasValue))
            {
                index = item.TabIndex.Value;
                if (index > highest)
                {
                    highest = index;
                }

                result.Add(index, item);
            }

            index = highest;
            foreach (var item in list.Where(x => !x.TabIndex.HasValue))
            {
                result.Add(++index, item);
            }

            return result;
        }

        private static List<IAttributeTabIndex> ToTabIndexedList(IInnerElements obj)
        {
            List<IAttributeTabIndex> result = new List<IAttributeTabIndex>();

            if (null != obj)
            {
                foreach (IElement element in obj.Elements)
                {
                    result.AddRange(ToTabIndexedList(element as IInnerElements));
                    IAttributeTabIndex indexed = element as IAttributeTabIndex;
                    if (null != indexed)
                    {
                        result.Add(indexed);
                    }
                }
            }

            return result;
        }

        private static void DisplayErrors(IInnerElements obj)
        {
            if (null != obj)
            {
                foreach (IElement element in obj.Elements)
                {
                    DisplayErrors(element as IInnerElements);
                    IValidation control = element as IValidation;
                    if (null != control && !control.IsValid && null != control.Validator)
                    {
                        control.Validator.DisplayErrors();
                    }
                }
            }
        }
    }
}